不知道有沒有人記得 DAY1 文章說本篇使用 .NET 平台,結果一直到第 29 天都還沒有寫到任何有關的東西,其實原本是想全程使用 .NET 來舉例,但考量到觀看者不一定使用 C#,還是乖乖回頭用 javascript 來說明。
會寫這一篇呢是想幫助過去的我自己,當時使用的語言是 C#,因專案要接觸 MongoDB,在語法上著實惱了一陣子,明明是很簡單的東西卻不習慣使用 Mongo C# Driver,導致額外花了不少時間在熟悉工具,要是有這一篇的話,肯定能快速上手(賣瓜?)
本篇文章會像是 Cheat sheet 性質,直接給予語法,一個速食的概念,實際應用在專案上,可以再重構得更漂亮些。另外本篇使用的語法都是強型別為主,畢竟使用 C# 就要發揮它的特性,如果使用字串代替欄位,雖然開發期很輕鬆快速,但很容易因為 typo 導致要抓 bug 抓很久,別問我為什麼知道,因為開發初期就吃了很多苦XD
以下的範例我們會使用下面這個 entity 來當作舉例
[BsonIgnoreExtraElements]
public class SampleClassEntity
{
[BsonId]
public string Id { get; set;}
public int IntField { get; set;}
public string StringField { get; set;}
}
[BsonIgnoreExtraElements]
是忽略沒有 mapping 到 c# 端的欄位
[BsonId]
是 Mongo C# driver 的 attribute,代表這個 property 是對應到 MongoDB _id
欄位。
const string MongoAddress = "mongodb://localhost:27017";
const string HelloDatabase = "hellomongodb";
const string Collection = "sample";
var client = new MongoClient(MongoAddress);
public IMongoCollection<SampleClassEntity> GetCollection()
{
var _client = new MongoClient(MongoAddress);
return _client.GetDatabase(HelloDatabase).GetCollection<SampleClassEntity>(Collection);
}
await MongoHelper.GetCollection().InsertOneAsync(new SampleClassEntity());
一次建立多筆(bulk write)會在 Update 那邊一起使用,即 MongoDB 的 upsert
var builder = Builders<SampleClassEntity>.Filter;
var dbFilter = builder.Gt(x => x.IntField, 1)
& builder.Eq(x => x.StringField, "ComparedString");
首先我們建立一個 Filter 類型的 builder,接著拼湊出我們要的查詢條件。
以上面的範例是 IntField > 1
且 StringField == ComparedString
var someCondition = true;
if (someCondition)
dbFilter &= builder.Gt(x => x.IntField, 2);
也會有一些狀況是特定條件下才會有的,上面是如果 someCondition == true
,那就會需要多家這個過濾條件。
var result = await MongoHelper.GetCollection().Find(dbFilter).ToListAsync();
接著我們就可以將此 filter 放入 Find()
內當參數使用。
這邊的 await 與 ToListAsync 是非同步用法,如果是同步的呼叫那就不需要加上去。
// Find by cursor
var proj = Builders<SampleClassEntity>.Projection
.Include(x => x.Id)
.Include(x => x.IntField);
using var cursor = await MongoHelper.GetCollection()
.WithReadPreference(ReadPreference.SecondaryPreferred)
.FindAsync(dbFilter, new FindOptions<SampleClassEntity>
{
Sort = Builders<SampleClassEntity>.Sort.Descending(x => x.Id),
Skip = 1,
Limit = 2,
Projection = proj
});
var result = cursor.ToEnumerable().ToArray();
這邊除了使用 cursor 方式之外多了一些東西。
Projection
Include
與 Exclude
WithReadPreference(ReadPreference.SecondaryPreferred)
ReadPreference
enum 定義了那些FindOptions
更新分為 Upsert 與 Replace 兩個項目來講,但本質上不會差太多的,一個是更新(設定)特定欄位,另一個是整個物件取代。
這邊的範例是直接用 BulkWrite 方式,批量進行修改了。
var src = Array.Empty<SampleClassEntity>();
var bulks = src.Select(entity =>
{
return new UpdateOneModel<SampleClassEntity>
(
Builders<SampleClassEntity>.Filter.Eq(x => x.Id, ""),
Builders<SampleClassEntity>.Update
.Inc(x => x.IntField, 1)
.Set(x => x.StringField, "2")
) {IsUpsert = true};
}).ToArray();
await MongoHelper.GetCollection().BulkWriteAsync(bulks, new BulkWriteOptions{IsOrdered = false});
Update 的方式須特別指定要更新的欄位,這邊舉了Inc
即 Increment,增加數量的意思Set
更新成新值SetOnInsert
是在第一次寫入時才會使用該值,後面任何 Update 都不會修改IsOrdered
整批量個更新不需要按順序執行(之前文章有提到)
var src = Array.Empty<SampleClassEntity>();
var bulks = src.Select(x =>
{
var replace = new ReplaceOneModel<SampleClassEntity>(
Builders<SampleClassEntity>.Filter.Eq(m => m.Id, x.Id), x)
{IsUpsert = true};
return replace;
}).ToArray();
await MongoHelper.GetCollection().BulkWriteAsync(bulks);
Replace 相對單純一些,只需要 Filter 找到特定的文件,進行整個內容取代。
var filter = Builders<SampleClassEntity>.Filter.In(x => x.Id, new[] {"ToDeleteId"});
await MongoHelper.GetCollection().DeleteManyAsync(filter);
刪除部分是直接用 DeleteMany
,可以按需求使用 DeleteOne
。
最後還是要強調,這只是語法的演示,實務上需要再調整寫法與效能。
本系列文章會同步發表於我個人的部落格 Pie Note